#ifndef THREAD_QUEUE_H_
#define THREAD_QUEUE_H_

#include <deque>
#include <mutex>
#include <condition_variable>
#include <chrono>

template<typename T>
class Queue
{
public:
	template <typename Element>
	void push(Element&& element)
	{
		std::lock_guard<std::mutex> lg(mtx);
		data.push_back(std::forward<Element>(element));
		cv.notify_one();
	}

	bool try_pop(T& element)
	{
		std::lock_guard<std::mutex> lg(mtx);
		if (data.empty())
		{
			return false;
		}
		element = std::move(data.front());
		data.pop_front();
		return true;
	}

	void wait_pop(T& t)
	{
		std::unique_lock<std::mutex> lock(mtx);
		cv.wait(lock, [this]() { return !data.empty(); });
		t = std::move(data.front());
		data.pop_front();
	}

	bool timeout_pop(T& t, size_t time_out_ms = 10)
	{
		std::unique_lock<std::mutex> lock(mtx);
		bool has_value = cv.wait_for(lock, std::chrono::milliseconds(time_out_ms), [this]() { return !data.empty(); });
		if (!has_value)
		{
			return false;
		}
		t = std::move(data.front());
		data.pop_front();
		return true;
	}

	bool empty()
	{
		std::lock_guard<std::mutex> lg(mtx);
		return data.empty();
	}

	size_t size()
	{
		std::lock_guard<std::mutex> lg(mtx);
		return data.size();
	}

private:
	std::mutex mtx;
	std::condition_variable cv;
	std::deque<T> data;
};

#endif